﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace LibEBML.Common
{
    public class Cluster : MatroskaBase
    {
        public Cluster(EBMLContainer container)
            : base(container)
        {
           
        }
        protected override void Initialize()
        {
            var tc = container[0xe7].FirstOrDefault() as EBMLType<ulong>;
            if (tc != null)
            {
                TimeCode = (long)tc.Value;
            }
            var b = new List<SimpleBlock>();
            var blocks = container[0xa0];
            var bs = blocks.Select(m => new Block(m as EBMLContainer) as SimpleBlock);
            var simpleblocks = container[0xa3];
            b.AddRange(bs);
            b.AddRange(simpleblocks.Select(m => new SimpleBlock(m as EBMLBinary)));
            Blocks = b;
        }
        protected override void InitDefaultValues()
        {
            
        }
        public long TimeCode { get; protected set; }
        public IList<SimpleBlock> Blocks { get; protected set; }
    }
    public class Block : SimpleBlock
    {
        public virtual long ReferenceBlock { get; protected set; }
        public Block(EBMLContainer blockgroup)
            : base(blockgroup[0xa1].FirstOrDefault() as EBMLBinary)
        {
            container = blockgroup;
            var tc = container[0xfb].FirstOrDefault() as EBMLType<long>;
            if (tc != null)
            {
                ReferenceBlock = (long)tc.Value;
            }
        }
        EBMLContainer container = null;
        protected override void Initialize()
        {

            base.Initialize();
        }

    }

    public class SimpleBlock
    {
        public virtual long TrackNumber { get; protected set; }
        public virtual long TimeCode { get; protected set; }
        public virtual bool Inverse { get; protected set; }
        public virtual int LacingType { get; protected set; }


        public virtual IList<byte[]> GetBlockData(byte[] ContentCompression)
        {
            int cc = 0;
            if (ContentCompression != null)
            {
                cc = ContentCompression.Length;
            }
            Source.Position = Position + FramePosition;
            byte[] b = new byte[cc + FrameTotalSize];
            if (ContentCompression != null)
            {
                Buffer.BlockCopy(ContentCompression, 0, b, 0, cc);
            }
            int i = Source.Read(b, cc, (int)FrameTotalSize);
            i += cc;
            int i2 = 0;
            List<byte[]> Result = new List<byte[]>();
            int framecount = 1;
            switch (LacingType)
            {
                case 0x00://No
                    //Console.WriteLine("No Lac");
                    Result.Add(b);

                    break;
                case 0x02://Xiph
                    // Console.WriteLine("Xiph Lac");

                    break;
                case 0x04://Fixed
                    // Console.WriteLine("Fixed Lac");
                    framecount = b[i2] + 1;
                    //framesizes = new long[framecount];
                    int fs = 0;
                    i2++;
                    fs = (i - i2) / framecount;
                    for (int i3 = i2; i3 < i; i3 += fs)
                    {
                        byte[] tmp2 = new byte[fs];
                        Buffer.BlockCopy(b, i3, tmp2, 0, fs);
                        Result.Add(tmp2);
                    }
                    break;
                case 0x06://EBML

                    // Console.WriteLine("EBML Lac");
                    framecount = b[i2] + 1;
                    long[] framesizes = new long[framecount];
                    i2++;
                    var tmp1 = ElementFactory.GetVintSize(b, i2, b.Length);
                    i2 += tmp1.Length;
                    framesizes[0] = ElementFactory.ConvertVintToLong(tmp1);
                    long lastsize = framesizes[0], totalsize = framesizes[0];
                    for (int vs = 1; vs < framecount - 1; vs++)
                    {
                        var cursizeblock = ElementFactory.GetVintSize(b, i2, b.Length);
                        var cursize = ElementFactory.ConvertVintToLongSign(cursizeblock) + lastsize;
                        framesizes[vs] = cursize;
                        i2 += cursizeblock.Length;
                        lastsize = cursize;
                        totalsize += cursize;
                    }
                    framesizes[framecount - 1] = b.Length - i2 - totalsize;
                    foreach (var s in framesizes)
                    {
                        byte[] tmp2 = new byte[s];
                        Buffer.BlockCopy(b, i2, tmp2, 0, tmp2.Length);
                        Result.Add(tmp2);
                        i2 += (int)s;
                    }
                    break;
                default:
                    break;
            }
            return Result;
        }
        protected long FramePosition, FrameTotalSize;
        protected virtual void Initialize()
        {
            byte[] b = new byte[2048];
            Source.Position = Position;
            int i = Source.Read(b, 0, b.Length);
            var trackid = ElementFactory.GetVintSize(b, 0, b.Length);
            var tid = ElementFactory.ConvertVintToLong(trackid);
            TrackNumber = tid;
            int i2 = trackid.Length;
            var tc = BitConverter.ToInt16(new[] { b[i2 + 1], b[i2] }, 0);
            TimeCode = tc;
            // Console.WriteLine("TrackID:={0},BlockTimeCode:={1}", tid, tc);
            byte flag = b[i2 + 2];
            Inverse = (flag & 0x08) > 0;
            var lac = flag & 0x06;
            LacingType = lac;
            i2 += 3;
            FramePosition = i2;
            FrameTotalSize = Length - i2;
        }
        public SimpleBlock(EBMLBinary blockdata)
        {
            if (blockdata != null)
            {
                Source = blockdata.BaseSource;
                Position = blockdata.DataPosition;
                Length = blockdata.Size;
                Initialize();
            }
            else
            {
                throw new ArgumentNullException("缺少Block数据");
            }

        }
        public virtual System.IO.Stream Source { get; protected set; }
        public virtual long Position { get; protected set; }
        public virtual long Length { get; protected set; }
    }



}
